#include "scanner.h"

// ctor
Scanner::Scanner(string input) : input(input), row(1), column(1)
{
    this->initAutomat();
}

Scanner::Scanner() : input(input), row(1), column(1)
{
    this->input = "";
    this->initAutomat();
}

void Scanner::setInput(string _input)
{
    this->input.assign(_input);
}



void Scanner::initAutomat()  // inicializacija tabele prehodov in tabele koncnih stanj
{
    // tabela prehodov je prazna
    for(int i=0; i<=MAX_STATE; i++)
    {
            for(int j=0; j<256; j++)
            automata[i][j] = NO_EDGE;
    }

    // ce smo v stanju 0 ali 1 ob cifri, gremo v stanje 1
    for(int i='0'; i<='9'; i++)
            automata[0][i] = automata[1][i] = 1;

    // pri '.' tudi gremo v stanje 1
    automata[0]['.'] =  automata[1]['.'] = 1;

    // ce smo v stanju 0 ob + * = - ^ / gremo v stanje 2
    automata[0]['+'] = automata[0]['*'] = automata[0]['='] = automata[0]['-'] = automata[0]['/'] = automata[0]['^'] = 2;

    // ce smo v stanju o ob ( ), gremo v stanje 3
    automata[0]['('] = automata[0][')'] = 3;

    // identifikator, gremo v 4
    automata[0]['x'] = 4;

    // ce smo v stanju 0 ali 5 ob znaku, gremo v stanje 5
    for(int i='a'; i<'x'; i++)  // x je identifikator, y in z nista v abecedi
            automata[0][i] = automata[5][i] = 5;

    /* for(int i='a'; i<='z'; i++)
            automata[0][i] = automata[5][i] = 5;*/

    // ce smo v stanju 0 ob praznem mestu, gremo v 6
    automata[0]['\n'] = automata[0][' '] = automata[0]['\t'] = 6;

    // ce smo v stanju 6 ob praznem mestu, gremo v stanje 6
    automata[4]['\n'] = automata[4][' '] = automata[4]['\t'] = 6;

    ////////////////////////////

    // stanje 0 ni koncno, vrnemo leks. napako
    finite[0] = LEXERROR; // this->tLexError;

    // stanje 1 je koncno, vrnemo osnovni leks. simbol integer
    finite[1] = NUMBER; //this->tInteger;

    // stanje 2 je koncno, vrnemo os. leks. simb. operator
    finite[2] = OPERATOR; //this->tOperator;

    // stanje 4 je koncno, vrnemo leks. simb. separator
    finite[3] = SEPARATOR; //this->tSeparator;

    // stanje 4 je koncno, vrnemo leks. simb. identifikator
    finite[4] = IDENTIFICATOR; //this->tSeparator;

    // stanje 5 je koncno, vrnemo leks. simb. niz
    finite[5] = STRING; //this->tSeparator;

    // stanje 4 je koncno, to se prezre
    finite[6] = IGNORE; //this->tIgnore;

    // rezervirane besede
    this->reswdWords[0] = "sin";
    this->reswdWords[1] = "cos";
    this->reswdWords[2] = "abs";
}

bool Scanner::isReservedWord(string s)
{
    for(int i=0; i<RESERWED_WORDS; i++)
        if(reswdWords[i] == s)
            return true;

    return false;
}

// naaslednje stanje vemo, ce poznamo trenutno stanje in znak na vhodu
// z pogledom v tabelo
int Scanner::getNextState(int aState, int aChar) const
{
    if(aChar == -2)
            return NO_EDGE;  // in case of -2, there is absolutely no edge

    return this->automata[aState][aChar]; // otherwise, get next state in the table
}

// ali je koncno stanje?
bool Scanner::isFiniteState(int aState) const
{
    return this->finite[aState] != LEXERROR;
}

int Scanner::getFiniteState(int aState) const
{
    return finite[aState];
}

// read next character
int Scanner::read()
{
    int firstChar = (int)input[0]; // beri prvi znak

    // pobrisi ga
    input.erase(0, 1);
    column ++; // stolpec ++
	
	// newline
    if(firstChar == '\n')
    {
            row ++; // vrstica = vrstica +1
            column = 1; // stolpec = 1;
    }

    return firstChar; // vrni prebrani prvi znak
}

bool Scanner::eof()
{
    return input.size() == 0 ? true : false; //peek() == -1; // ce je znak, ki ga preberemom, leks. -1, smo na koncu
}

int Scanner::peek()
{
    //    return input->peek(); // samp prebere znak v inputu in ga vrne, ga ne brise
    return (int)input[0]; //[input.size()-1];
}

Token Scanner::nextToken()
{
    return lastToken = nextTokenImp();
}

Token Scanner::currentToken()
{
    return lastToken;
}


Token Scanner::nextTokenImp()
{
    int currentState = START_STATE;
    string lexem;

    int startColumn = this->column;
    int startRow = this->row;

    do
    {
            // ob trenutnem stanju in vhodnemu znaku pojdi v novo stanje
            int tempState = this->getNextState(currentState, peek());

            if (tempState != NO_EDGE)
            {
            	// prehod v novo stanje mozen
            	currentState = tempState;

            	// zdruzi prebrani znak v lexem
            	lexem += this->peek();
            	(char)this->read();
            }
            else
            {
            	// prehod ni mozen; ali je stanje koncno?
            	if(this->isFiniteState(currentState))
            	{
                    // stanje je koncno in vracamo osonovni leks. simbol
                    // se je lex. simbol niz, preverimo, ce je rezervirana beseda
                    Token token(lexem, startColumn, startRow, this->getFiniteState(currentState), eof());

                    if(token.getType() == IGNORE)
                    	// presledke in nove vrstice prezremo
                    	return this->nextToken();
                    else
                    {
                    	if(currentState == STRING)
                    	{
                            if(!this->isReservedWord(lexem))
                            return Token("ERROR: " + lexem + " ni rezervirana beseda.", startColumn, startRow,  LEXERROR, eof()); // ce ni, vrnemo sint. napako
                    	}

                    	return token;
                    }
            	}
            else
                    // stanje ni koncno, vrnemo leks. napako
                    return Token("ERROR:" + lexem, startColumn, startRow,  LEXERROR, eof());

            }
    } while(true);
}




